#!/usr/bin/env python3
import os
import sys
import platform
import io
from pathlib import Path
from urllib import parse as urlparser
import requests
import argparse
import configparser
import subprocess
import tempfile
import json

# CONSTANTS
LOGICAL_TRUE = ('true', '1', 't', '+')


def recursive_remove(directory: Path):
    for fobj in directory.iterdir():
        if fobj.is_dir():
            recursive_remove(fobj)
        else:
            fobj.unlink()
    directory.rmdir()


parser = argparse.ArgumentParser()
command_parsers = parser.add_subparsers(title='Commands',
                                        description='Repository configuration commands for IPFS remote target',
                                        dest='commands')
# CHECK
check_parser = command_parsers.add_parser('check',
                                          help='Checks the accessibility of IPFS API and validity of credentials')
# RELEASE
bundle_parser = command_parsers.add_parser('release',
                                           help='Creates and uploads to IPFS the bundle (release) of repository files addressed by the given commit ID or tag')
bundle_parser.add_argument('tag',
                           action='store',
                           nargs=1,
                           help='Tag or commit ID to release from',
                           default=None)
bundle_parser.add_argument('ipns_key',
                           action='store',
                           nargs='?',
                           help='IPNS peer name to publish the release',
                           default=argparse.SUPPRESS)
bundle_parser.add_argument('--prefix',
                           action='store',
                           nargs='?',
                           help='Version prefix for being used as a name for the archive',
                           default='release')
bundle_parser.add_argument('-p', '--publish',
                           action='store_true',
                           help='Publish the release over IPNS after uploading')
bundle_parser.add_argument('-z', '--zip',
                           action='store_true',
                           help='Use ZIP archive format instead of .tar.gz')
# CLONE
clone_parser = command_parsers.add_parser('clone',
                                          help='Clones a repository from IPFS entry with all infrastructure scripts already installed')
clone_parser.add_argument('ipfs_id',
                          action='store',
                          nargs=1,
                          help='IPFS CID or IPNS peer name to use as remote ID')
clone_parser.add_argument('directory',
                          action='store',
                          nargs=1,
                          help='Relative directory to clone the repository in')
clone_parser.add_argument('api_url',
                          action='store',
                          nargs=1,
                          help='IPFS node API URL (API must be active to view the remote Git database!), default is %(default)s',
                          default='http://127.0.0.1')
clone_parser.add_argument('api_port',
                          action='store',
                          nargs='?',
                          help='IPFS node API port (will be attached to URL) [%(default)s]',
                          default='5001')
clone_parser.add_argument('-t', '--timeout',
                          action='store',
                          required=False,
                          help='Network timeout for API communications, sec (float)',
                          default='30')
clone_parser.add_argument('-r', '--remote-name',
                          action='store',
                          required=False,
                          nargs='?',
                          help='Gives the remote name to make an IPFS remote, default is %(default)s',
                          default='origin')
clone_parser.add_argument('-b', '--branch',
                          action='store',
                          required=False,
                          nargs='?',
                          help='Gives name of the branch to checkout',
                          default=None)
clone_parser.add_argument('-n', '--username',
                          action='store',
                          required=False,
                          nargs='?',
                          help='HTTP authentication username',
                          default=None)
clone_parser.add_argument('-p', '--password',
                          action='store',
                          required=False,
                          nargs='?',
                          help='HTTP authentication password',
                          default=None)
# INSTALL
install_parser = command_parsers.add_parser('install',
                                            help='Installs infrastructure scripts to enable IPFS directory as remote repository')
install_parser.add_argument('api_url',
                            action='store',
                            nargs=1,
                            help='IPFS node API URL (API must be active to view the remote Git database!), default is %(default)s',
                            default='http://127.0.0.1')
install_parser.add_argument('api_port',
                            action='store',
                            nargs='?',
                            help='IPFS node API port (will be attached to URL) [%(default)s]',
                            default='5001')
install_parser.add_argument('ipfs_id',
                            action='store',
                            nargs='?',
                            help='IPFS CID or IPNS peer name to use as remote ID',
                            default=argparse.SUPPRESS)
install_parser.add_argument('-t', '--timeout',
                            action='store',
                            required=False,
                            help='Network timeout for API communications, sec (float), default %(default)s',
                            default='30.0')
install_parser.add_argument('-r', '--remote-name',
                            action='store',
                            required=False,
                            nargs='?',
                            help='Gives the remote name to make an IPFS remote, default is %(default)s',
                            default='origin')
install_parser.add_argument('-n', '--username',
                            action='store',
                            required=False,
                            nargs='?',
                            help='HTTP authentication username',
                            default=None)
install_parser.add_argument('-p', '--password',
                            action='store',
                            required=False,
                            nargs='?',
                            help='HTTP authentication password',
                            default=None)
install_parser.add_argument('--check-ipfs',
                            action='store_true',
                            required=False,
                            help='Check IPFS API and authentication after installation')
uninstall_parser = command_parsers.add_parser('uninstall', help='Uninstalls IPFS remote infrastructure scripts')
uninstall_parser.add_argument('-p', '--purge',
                              action='store_true',
                              required=False,
                              help='Purge all additional IPFS infrastructure from the repository')
uninstall_parser.add_argument('-r', '--remote-name',
                              action='store',
                              required=False,
                              nargs='?',
                              help='Gives the remote name to restore or remove',
                              default='origin')
# CONFIG
config_parser = command_parsers.add_parser('config',
                                           help='Contains configuration commands for the project with IPFS remote installed')
config_parsers = config_parser.add_subparsers(title='Configuration commands',
                                              description='Reconfiguration commands to maintain the repository with IPFS remote target installed',
                                              dest='commands_config')
print_parser = config_parsers.add_parser('print',
                                         help='Prints current configuration on the standard output')
# Generate
generate_parser = config_parsers.add_parser('generate',
                                            help='Generates/overwrites new IPFS remote config (use --stdout to print it back to the screen)')
generate_parser.add_argument('-s', '--stdout',
                             action='store_true',
                             required=False,
                             help='Print the generated configuration file to the standard output instead of overwriting the existing one')
generate_parser.add_argument('-c', '--default',
                             action='store_true',
                             required=False,
                             help='Generate new configuration file overriding all other values set up in this command with their defaults')
generate_parser.add_argument('-f', '--filename',
                             action='store',
                             required=False,
                             help='Optional filename to store the generated configuration (default is .git/ipfs/config)',
                             default=None,
                             nargs='?')
generate_parser.add_argument('-y', '--overwrite',
                             action='store_true',
                             required=False,
                             help='Overwrite existing configuration',
                             default=False)
generate_parser.add_argument('--url',
                             action='store',
                             required=False,
                             nargs='?',
                             default='http://127.0.0.1',
                             help='IPFS node API URL to locate (API must be active to view the remote Git database!), default is \'%(default)s\'')
generate_parser.add_argument('--port',
                             action='store',
                             required=False,
                             nargs='?',
                             default='5001',
                             help='IPFS node API TCP port (will be attached to URL), default is %(default)s')
generate_parser.add_argument('--api-version-prefix',
                             action='store',
                             required=False,
                             nargs='?',
                             default='api/v0',
                             help='IPFS node API version prefix (CAUTION! It is NOT RECOMMENDED to change the default value \'%(default)s\'!!!)')
generate_parser.add_argument('-t', '--timeout',
                             action='store',
                             required=False,
                             nargs='?',
                             help='Network timeout for API communications, sec (float), default is %(default)s',
                             default='30.0')
generate_parser.add_argument('--unpin-old',
                             action='store_true',
                             required=False,
                             help='Instructs the IPFS node whether it is needed to unpin the old immutable repository state from the drive or not',
                             default=False)
generate_parser.add_argument('-r', '--republish',
                             action='store_true',
                             required=False,
                             help='Instructs the IPFS node whether it is needed to republish the freshly pushed commits under the given IPNS name (if it is available)',
                             default=False)
generate_parser.add_argument('--ttl',
                             action='store',
                             required=False,
                             nargs='?',
                             help='Period description string for IPNS republisher, default is \'%(default)s\'',
                             default='24h')
generate_parser.add_argument('-v', '--cid-version',
                             action='store',
                             required=False,
                             nargs='?',
                             choices=['0', '1'],
                             help='CID version used to create immutable entries, default is %(default)s',
                             default='1')
generate_parser.add_argument('-n', '--username',
                             action='store',
                             required=False,
                             nargs='?',
                             help='HTTP authentication username',
                             default=None)
generate_parser.add_argument('-p', '--password',
                             action='store',
                             required=False,
                             nargs='?',
                             help='HTTP authentication password',
                             default=None)
# Manage
manage_parser = config_parsers.add_parser('manage',
                                          help='Adjusts existing IPFS remote config. NOTE: for most options leaving it without a value resets the corresponding parameter to default')
manage_parser.add_argument('--dry-run',
                           action='store_true',
                           required=False,
                           help='Show the regenerated configuration on standard output instead of overwriting the config file')
manage_parser.add_argument('--reset',
                           action='store_true',
                           required=False,
                           help='Reset the entire configuration to defaults, overrides any other option here')
manage_parser.add_argument('--url',
                           action='store',
                           required=False,
                           nargs='?',
                           const='http://127.0.0.1',
                           default=argparse.SUPPRESS,
                           help='IPFS node API URL to locate (API must be active to view the remote Git database!)')
manage_parser.add_argument('--port',
                           action='store',
                           required=False,
                           nargs='?',
                           const='5001',
                           default=argparse.SUPPRESS,
                           help='IPFS node API TCP port (will be attached to URL)')
manage_parser.add_argument('--api-version-prefix',
                           action='store',
                           required=False,
                           nargs='?',
                           const='api/v0',
                           default=argparse.SUPPRESS,
                           help='IPFS node API version prefix (CAUTION! It is NOT RECOMMENDED to change the default value \'api/v0\'!!!)')
manage_parser.add_argument('-t', '--timeout',
                           action='store',
                           required=False,
                           nargs='?',
                           help='Network timeout for API communications, sec (float)',
                           default=argparse.SUPPRESS,
                           const='30.0')
manage_parser.add_argument('--unpin-old',
                           action='store',
                           required=False,
                           nargs='?',
                           help='Instructs the IPFS node whether it is needed to unpin the old immutable repository state from the drive or not',
                           default=argparse.SUPPRESS,
                           const='True')
manage_parser.add_argument('-r', '--republish',
                           action='store',
                           nargs='?',
                           const='True',
                           required=False,
                           help='Instructs the IPFS node whether it is needed to republish the freshly pushed commits under the given IPNS name (if it is available)',
                           default=argparse.SUPPRESS)
manage_parser.add_argument('--ttl',
                           action='store',
                           required=False,
                           nargs='?',
                           help='Period description string for IPNS republisher',
                           const='24h',
                           default=argparse.SUPPRESS)
manage_parser.add_argument('-v', '--cid-version',
                           action='store',
                           required=False,
                           nargs='?',
                           choices=['0', '1'],
                           help='CID version used to create immutable entries',
                           default=argparse.SUPPRESS,
                           const='1')
manage_parser.add_argument('-n', '--username',
                           action='store',
                           required=False,
                           nargs='?',
                           help='HTTP authentication username',
                           const=None,
                           default=argparse.SUPPRESS)
manage_parser.add_argument('-p', '--password',
                           action='store',
                           required=False,
                           nargs='?',
                           help='HTTP authentication password',
                           default=argparse.SUPPRESS,
                           const=None)
# Edit
config_parsers.add_parser('edit', help='Edit the IPFS remote configuration manually')
# CLI
arguments = parser.parse_args()
# No command
if arguments.commands is None:
    print('Please specify a command!')
    parser.print_help()
    sys.exit(0)
# Clone / ready repo
if not (arguments.commands == 'clone' or (arguments.commands == 'config' and arguments.commands_config == 'generate')):
    # print('CAUTION! IPFS management operation you selected requires git repository already initialized!')
    project_dir = None
    try:
        project_dir = Path(
            subprocess.check_output(['git', 'rev-parse', '--show-toplevel']).decode('utf-8').rstrip()).absolute()
    except subprocess.CalledProcessError:
        print(
            'Error: it does not seem that you call git-ipfs from inside the Git local repository.\nPlease choose the right working directory')
        sys.exit(1)
    git_dir = Path(project_dir / Path('.git'))
    git_ipfs_dir = Path(git_dir / Path('ipfs'))
    git_ipfs_config_file = Path(git_ipfs_dir / Path('config'))
    git_ipfs_old_url = Path(git_ipfs_dir / Path('old_url'))
    if not git_ipfs_config_file.is_file() and not (arguments.commands == 'install' or arguments.commands == 'uninstall' or (arguments.commands == 'config' and (arguments.commands_config == 'print' or arguments.commands_config == 'generate'))):
        print('''Cannot find IPFS remote configuration file!
Please run

    git ipfs install

to install all the necessary scripts.
NOTE: all configuration view/verification commands you use will use hardcoded default configuration''')
        sys.exit(1)

default_config = '''[IPFS]
URL = http://127.0.0.1
Port = 5001
VersionPrefix = api/v0
Timeout = 30.0
UnpinOld = False
Republish = True
IPNSTTLString = 24h
CIDVersion = 1
IPFSChunker = size-65536
# UserName = user
# UserPassword = password
'''
default_configuration = configparser.ConfigParser()
# Workaround for case-sensitive use case
# https://stackoverflow.com/questions/19359556/configparser-reads-capital-keys-and-make-them-lower-case
default_configuration.optionxform = str
default_configuration.read_string(default_config)
default_ipfs_configuration = default_configuration['IPFS']

#print(arguments)

if arguments.commands == 'check':
    default_configuration.read(git_ipfs_config_file)
    default_ipfs_configuration = default_configuration['IPFS']
    api_auth = None
    if 'UserName' in default_ipfs_configuration and 'UserPassword' in default_ipfs_configuration:
        api_auth = (default_ipfs_configuration['UserName'], default_ipfs_configuration['UserPassword'])
    ipfs_url = urlparser.urljoin(default_ipfs_configuration['URL'] + ':' + str(default_ipfs_configuration['Port']),
                                 urlparser.quote(default_ipfs_configuration['VersionPrefix']))
    if ipfs_url == default_ipfs_configuration['VersionPrefix']:
        print('Invalid IPFS API URL! Please check the values in the configuration')
        sys.exit(1)
    try:
        api_response = requests.post(ipfs_url + '/version', auth=api_auth, timeout=float(default_ipfs_configuration['Timeout']))
    except requests.exceptions.ConnectionError:
        print(
            'Cannot access IPFS API at {}!\nPlease check if you have a running node at the specified URL!'.format(
                ipfs_url))
        sys.exit(1)
    if api_response.status_code == 200:
        response_content = api_response.json()
        print('Found IPFS API node version {} at {}'.format(
            response_content['Version'] + '~' + response_content['Commit'],
            ipfs_url))
    else:
        print('Cannot access IPFS API at {}: HTTP response {}, abort'.format(ipfs_url, api_response.status_code))
        sys.exit(1)
    print('IPFS API accessibility checked successfully')

if arguments.commands == 'release':
    print('Releasing repository under the tag/commit {}'.format(arguments.tag))
    default_configuration.read(git_ipfs_config_file)
    default_ipfs_configuration = default_configuration['IPFS']
    tag_name = arguments.tag[0]
    file_name = arguments.prefix + '.tar.gz'
    archive_format = 'tar.gz'
    if arguments.zip:
        file_name = arguments.prefix + '.zip'
        archive_format = 'zip'
    with tempfile.TemporaryDirectory() as tmp_dir:
        tmp_path = Path(Path(tmp_dir) / Path(file_name))
        print(tmp_path)
        if subprocess.call(['git', 'archive', '-v', '--output', tmp_path, '--format', archive_format, tag_name]) != 0:
            print('Error creating archive')
            sys.exit(1)
        print('Uploading...')
        req_param = {
            'quiet': False,
            'quieter': False,
            'silent': False,
            'progress': False,
            'trickle': False,
            'only-hash': False,
            'wrap-with-directory': True,
            'chunker': default_ipfs_configuration['IPFSChunker'],
            'raw-leaves': True,
            'cid-version': default_ipfs_configuration['CIDVersion'],
            'pin': True
        }
        data = {file_name: tmp_path.open('rb').read()}
        api_auth = None
        if 'UserName' in default_ipfs_configuration and 'UserPassword' in default_ipfs_configuration:
            api_auth = (default_ipfs_configuration['UserName'], default_ipfs_configuration['UserPassword'])
        ipfs_url = urlparser.urljoin(default_ipfs_configuration['URL'] + ':' + str(default_ipfs_configuration['Port']),
                                     urlparser.quote(default_ipfs_configuration['VersionPrefix']))
        if ipfs_url == default_ipfs_configuration['VersionPrefix']:
            print('Invalid IPFS API URL! Please check the input values')
            sys.exit(1)
        wrapper_dir_hash = None
        try:
            api_response = requests.post(ipfs_url + '/add', params=req_param, files=data, auth=api_auth, timeout=float(default_ipfs_configuration['Timeout']))
            if api_response.status_code == 200:
                wrapper_dir_hash = \
                    json.JSONDecoder().decode(api_response.content.decode('utf-8').rstrip().split('\n')[-1])['Hash']
                print(
                    '\nNew immutable IPFS CID for release:\n\t{}'.format(wrapper_dir_hash))
        except requests.exceptions.ReadTimeout:
            print('Unable to upload data to IPFS node! Please check API accessibility!')
            sys.exit(1)
        if arguments.publish and 'ipns_key' in arguments and wrapper_dir_hash is not None:
            print('Attempting to publish the data under IPNS name:\n\t{}'.format(arguments.ipns_key))
            req_param = {
                'arg': wrapper_dir_hash,
                'resolve': True,
                'lifetime': default_ipfs_configuration['IPNSTTLString'],
                'allow-offline': True,
                'key': arguments.ipns_key,
                'quieter': False,
                'ipns-base': 'base36'
            }
            api_response = requests.post(ipfs_url + '/name/publish', params=req_param,
                                         timeout=float(default_ipfs_configuration['Timeout']), auth=api_auth)
            if api_response.status_code == 200:
                print('IPNS key updated. Please use it to share your release under name\n\t/ipns/{}\n'.format(arguments.ipns_key + '/' + file_name))
            else:
                print('ATTENTION! Unable to update IPNS key!\n\tPlease use new CID listed above to access the pushed release!')

if arguments.commands == 'install':
    print('Installing IPFS infrastructure for repository {}'.format(project_dir))
    auth_token = None
    if arguments.username is not None and arguments.password is not None:
        auth_token = (arguments.username, arguments.password)
    ipfs_url = urlparser.urljoin(arguments.api_url[0] + ':' + str(arguments.api_port),
                                 urlparser.quote(default_ipfs_configuration['VersionPrefix']))
    if ipfs_url == default_ipfs_configuration['VersionPrefix']:
        print('Invalid IPFS API URL! Please check the input values')
        sys.exit(1)
    default_ipfs_configuration['Timeout'] = str(float(arguments.timeout))
    default_ipfs_configuration['URL'] = arguments.api_url[0]
    default_ipfs_configuration['Port'] = str(arguments.api_port)
    if auth_token is not None:
        default_ipfs_configuration['UserName'] = auth_token[0]
        default_ipfs_configuration['UserPassword'] = auth_token[1]
    if not git_ipfs_dir.is_dir():
        git_ipfs_dir.mkdir(parents=True, exist_ok=True)
    if git_ipfs_config_file.is_file():
        print('''Attempt to install IPFS infrastructure over the existing installation!
Please uninstall the previous configuration to repeat the installation''')
        sys.exit(1)
    cfw = git_ipfs_config_file.open('w', encoding='utf-8')
    default_configuration.write(cfw)
    cfw.close()
    ipfs_id = None
    if 'ipfs_id' not in arguments:
        with tempfile.TemporaryDirectory() as tmp_dir:
            data = {}
            tmp_path = Path(tmp_dir)
            if subprocess.call(['git', 'init', '--bare', tmp_path]) != 0:
                print('Error creating nesting repository on IPFS side')
                sys.exit(1)
            for fobj in tmp_path.rglob('*'):
                if Path(fobj).is_dir():
                    data[str(Path(fobj).relative_to(tmp_path))] = None
                else:
                    data[str(Path(fobj).relative_to(tmp_path))] = Path(fobj).open('rb').read()
            req_param = {
                'quiet': False,
                'quieter': False,
                'silent': False,
                'progress': False,
                'trickle': False,
                'only-hash': False,
                'wrap-with-directory': True,
                'chunker': default_ipfs_configuration['IPFSChunker'],
                'raw-leaves': True,
                'cid-version': default_ipfs_configuration['CIDVersion'],
                'pin': True
            }
            api_auth = None
            if 'UserName' in default_ipfs_configuration and 'UserPassword' in default_ipfs_configuration:
                api_auth = (default_ipfs_configuration['UserName'], default_ipfs_configuration['UserPassword'])
            ipfs_url = urlparser.urljoin(
                default_ipfs_configuration['URL'] + ':' + str(default_ipfs_configuration['Port']),
                urlparser.quote(default_ipfs_configuration['VersionPrefix']))
            if ipfs_url == default_ipfs_configuration['VersionPrefix']:
                print('Invalid IPFS API URL! Please check the input values')
                sys.exit(1)
            wrapper_dir_hash = None
            try:
                api_response = requests.post(ipfs_url + '/add', params=req_param, files=data, auth=api_auth,
                                             timeout=300)
                if api_response.status_code == 200:
                    wrapper_dir_hash = \
                        json.JSONDecoder().decode(api_response.content.decode('utf-8').rstrip().split('\n')[-1])['Hash']
                    print(
                        '\nNew immutable IPFS CID for release:\n\t{}'.format(wrapper_dir_hash))
            except requests.exceptions.ReadTimeout:
                print('Unable to upload data to IPFS node! Please check API accessibility!')
                sys.exit(1)
            if wrapper_dir_hash is not None:
                ipfs_id = wrapper_dir_hash
    else:
        ipfs_id = arguments.ipfs_id
    git_remotes = subprocess.check_output(['git', 'remote']).decode('utf-8').strip().split('\n')
    if git_remotes is None or arguments.remote_name not in git_remotes:
        print('Installing remote {}'.format(arguments.remote_name))
        if subprocess.call(['git', 'remote', 'add', arguments.remote_name, 'ipfs://' + ipfs_id]) != 0:
            print('Error assigning remote!')
            sys.exit(1)
        remote_url = arguments.remote_name + ' -- NEW'
        pfw = git_ipfs_old_url.open('w', encoding='utf-8')
        pfw.write(remote_url)
        pfw.close()
    else:
        remote_url = arguments.remote_name + ' -- ' + subprocess.check_output(
            ['git', 'remote', 'get-url', arguments.remote_name]).decode('utf-8').strip()
        pfw = git_ipfs_old_url.open('w', encoding='utf-8')
        pfw.write(remote_url)
        pfw.close()
        if subprocess.call(['git', 'remote', 'set-url', arguments.remote_name, 'ipfs://' + ipfs_id]) != 0:
            print('Error redirecting URL of {}! Please consider recloning the repository'.format(arguments.remote_name))
            sys.exit(1)
    if arguments.check_ipfs:
        print('IPFS check flag is set, attempting to test API endpoint...')
        try:
            api_response = requests.post(ipfs_url + '/version', auth=auth_token, timeout=10)
        except requests.exceptions.ConnectionError:
            print('''Cannot access IPFS API at URL {}!
Please check if you have a running node at the specified URL!'''.format(ipfs_url))
            sys.exit(1)
        if api_response.status_code == 200:
            response_content = api_response.json()
            print('Found IPFS API node version {} at {}'.format(
                response_content['Version'] + '~' + response_content['Commit'], ipfs_url))
        else:
            print('''Cannot access IPFS API at {}: 
    HTTP response {}, abort'''.format(ipfs_url, api_response.status_code))
            sys.exit(1)
    print('IPFS infrastructure successfully installed')

if arguments.commands == 'uninstall':
    print('Ordering uninstallation of IPFS infrastructure')
    if not git_ipfs_config_file.is_file():
        print('It seems IPFS infrastructure is not installed or uninstalled already')
        sys.exit(0)
    if git_ipfs_old_url.is_file():
        pfw = git_ipfs_old_url.open('r', encoding='utf-8')
        old_remote = pfw.read().strip().split(' -- ')
        pfw.close()
        if old_remote[1] == 'NEW':
            subprocess.call(['git', 'remote', 'remove', old_remote[0]])
        else:
            git_remotes = subprocess.check_output(['git', 'remote']).decode('utf-8').strip().split('\n')
            if git_remotes is None or old_remote[0] not in git_remotes:
                subprocess.call(['git', 'remote', 'add', old_remote[0], old_remote[1]])
            else:
                subprocess.call(['git', 'remote', 'set-url', old_remote[0], old_remote[1]])
    else:
        git_remotes = subprocess.check_output(['git', 'remote']).decode('utf-8').strip().split('\n')
        if git_remotes is None or arguments.remote_name not in git_remotes:
            print('Attempt to restore non-existent remote {}, dropping request'.format(arguments.remote_name))
        else:
            subprocess.call(['git', 'remote', 'remove', arguments.remote_name])
    if git_ipfs_config_file.is_file():
        git_ipfs_config_file.unlink(missing_ok=True)
    if arguments.purge:
        recursive_remove(git_ipfs_dir)
    print('IPFS infrastructure uninstalled')

if arguments.commands == 'clone':
    old_cwd = os.getcwd()
    print(old_cwd)
    git_new_repo_dir = Path(Path(old_cwd) / Path(arguments.directory[0]))
    git_new_repo_dir.mkdir(parents=True, exist_ok=True)
    print('Cloning to {}'.format(arguments.directory[0]))
    os.chdir(git_new_repo_dir)
    if subprocess.call(['git', 'init', '-q']) != 0:
        print('Error creating initial object structure for repository {}'.format(git_new_repo_dir))
        os.chdir(old_cwd)
        sys.exit(1)
    git_dir = Path(git_new_repo_dir / Path('.git'))
    git_ipfs_dir = Path(git_dir / Path('ipfs'))
    git_ipfs_config_file = Path(git_ipfs_dir / Path('config'))
    auth_token = None
    if arguments.username is not None and arguments.password is not None:
        auth_token = (arguments.username, arguments.password)
    ipfs_url = urlparser.urljoin(arguments.api_url[0] + ':' + str(arguments.api_port),
                                 urlparser.quote(default_ipfs_configuration['VersionPrefix']))
    if ipfs_url == default_ipfs_configuration['VersionPrefix']:
        print('Invalid IPFS API URL! Please check the input values')
        os.chdir(old_cwd)
        sys.exit(1)
    default_ipfs_configuration['Timeout'] = str(float(arguments.timeout))
    default_ipfs_configuration['URL'] = arguments.api_url[0]
    default_ipfs_configuration['Port'] = str(arguments.api_port)
    if auth_token is not None:
        default_ipfs_configuration['UserName'] = auth_token[0]
        default_ipfs_configuration['UserPassword'] = auth_token[1]
    if not git_ipfs_dir.is_dir():
        git_ipfs_dir.mkdir(parents=True, exist_ok=True)
    cfw = git_ipfs_config_file.open('w', encoding='utf-8')
    default_configuration.write(cfw)
    cfw.close()
    if subprocess.call(['git', 'remote', 'add', arguments.remote_name, 'ipfs://' + arguments.ipfs_id[0]]) != 0:
        print('Error assigning remote!')
        os.chdir(old_cwd)
        sys.exit(1)
    if subprocess.call(['git', 'fetch', '-f', '-q', arguments.remote_name]) != 0:
        print('Error fetching IPFS data!')
        os.chdir(old_cwd)
        sys.exit(1)
    head_reference = subprocess.check_output(['git', 'symbolic-ref', 'HEAD']).decode('utf-8').strip().split('/')
    if len(head_reference) > 1:
        subprocess.call(['git', 'checkout', '-q', head_reference[-1]])
    else:
        subprocess.call(['git', 'checkout', '-q', head_reference[0]])
    if arguments.branch is not None:
        if subprocess.call(['git', 'checkout', arguments.branch]) != 0:
            print('Error checking out to {}'.format(arguments.branch))
            os.chdir(old_cwd)
            sys.exit(0)
    print('IPFS repository cloned successfully')
    os.chdir(old_cwd)

if arguments.commands == 'config':
    if arguments.commands_config is None:
        print('Please specify configuration mode!')
        config_parser.print_help()
        sys.exit(0)
    if arguments.commands_config == 'print':
        if not git_ipfs_config_file.is_file():
            print('WARNING: IPFS remote is not initialized!\nThe default hard-coded configuration values will be printed out!')
        else:
            default_configuration.read(git_ipfs_config_file)
            default_ipfs_configuration = default_configuration['IPFS']
        print('')
        strout = io.StringIO()
        default_configuration.write(strout)
        strout.seek(0)
        print(strout.read())
        strout.close()

    if arguments.commands_config == 'generate':
        config_file = None
        if arguments.filename is not None:
            config_file = Path(arguments.filename)
        else:
            project_dir = None
            try:
                project_dir = Path(
                    subprocess.check_output(['git', 'rev-parse', '--show-toplevel']).decode(
                        'utf-8').rstrip()).absolute()
                git_dir = Path(project_dir / Path('.git'))
                git_ipfs_dir = Path(git_dir / Path('ipfs'))
                git_ipfs_config_file = Path(git_ipfs_dir / Path('config'))
                git_ipfs_old_url = Path(git_ipfs_dir / Path('old_url'))
                config_file = git_ipfs_config_file
            except subprocess.CalledProcessError:
                config_file = Path(Path(os.getcwd()) / Path('config')).absolute()
        if not config_file.is_absolute():
            config_file = Path(project_dir / config_file).absolute()
        if config_file.is_file() and not arguments.overwrite and not arguments.stdout:
            print(
                'Permission denied to overwrite the existing configuration file!\nPlease set the \'-y\' flag to overwrite anyway')
            sys.exit(1)
        if not arguments.default:
            default_ipfs_configuration['URL'] = arguments.url
            default_ipfs_configuration['Port'] = str(arguments.port)
            default_ipfs_configuration['VersionPrefix'] = arguments.api_version_prefix
            default_ipfs_configuration['Timeout'] = arguments.timeout
            default_ipfs_configuration['UnpinOld'] = str(arguments.unpin_old)
            default_ipfs_configuration['Republish'] = str(arguments.republish)
            default_ipfs_configuration['IPNSTTLString'] = arguments.ttl
            default_ipfs_configuration['CIDVersion'] = str(arguments.cid_version)
            if arguments.username is not None and arguments.password is not None:
                default_ipfs_configuration['UserName'] = arguments.username
                default_ipfs_configuration['UserPassword'] = arguments.password
        if arguments.stdout:
            print('')
            strout = io.StringIO()
            default_configuration.write(strout)
            strout.seek(0)
            print(strout.read())
            strout.close()
        else:
            print('Generating config in {}'.format(config_file))
            fobj = config_file.open('w', encoding='utf-8')
            default_configuration.write(fobj)
            fobj.close()

    if arguments.commands_config == 'manage':
        if not git_ipfs_config_file.is_file():
            print('Nothing to manage, it seems there is no config file! Exiting...')
            sys.exit(1)
        if not arguments.reset:
            default_configuration.read(git_ipfs_config_file)
            default_ipfs_configuration = default_configuration['IPFS']
            if 'url' in arguments:
                default_ipfs_configuration['URL'] = arguments.url
            if 'port' in arguments:
                default_ipfs_configuration['Port'] = str(arguments.port)
            if 'api_version_prefix' in arguments:
                default_ipfs_configuration['VersionPrefix'] = arguments.api_version_prefix
            if 'timeout' in arguments:
                default_ipfs_configuration['Timeout'] = str(float(arguments.timeout))
            if 'unpin_old' in arguments:
                default_ipfs_configuration['UnpinOld'] = str(arguments.unpin_old.lower() in LOGICAL_TRUE)
            if 'republish' in arguments:
                default_ipfs_configuration['Republish'] = str(arguments.republish.lower() in LOGICAL_TRUE)
            if 'ttl' in arguments:
                default_ipfs_configuration['IPNSTTLString'] = arguments.ttl
            if 'cid_version' in arguments:
                default_ipfs_configuration['CIDVersion'] = str(arguments.cid_version)
            if 'username' in arguments and 'password' in arguments:
                if arguments.username is not None and arguments.password is not None:
                    default_ipfs_configuration['UserName'] = arguments.username
                    default_ipfs_configuration['UserPassword'] = arguments.password
        else:
            print('Default config in {} will be regenerated'.format(config_file))
        if arguments.dry_run:
            print('')
            strout = io.StringIO()
            default_configuration.write(strout)
            strout.seek(0)
            print(strout.read())
            strout.close()
        else:
            print('Writing IPFS Git remote transceiver config in\n\t{}'.format(git_ipfs_config_file))
            fobj = git_ipfs_config_file.open('w', encoding='utf-8')
            default_configuration.write(fobj)
            fobj.close()

    if arguments.commands_config == 'edit':
        print('Opening editor')
        # https://stackoverflow.com/questions/434597/open-document-with-default-os-application-in-python-both-in-windows-and-mac-os
        if platform.system() == 'Darwin':  # MacOS
            subprocess.call(('open', git_ipfs_config_file))
        elif platform.system() == 'Windows':  # Windows
            os.startfile(git_ipfs_config_file)
        else:  # Linux
            subprocess.call(('xdg-open', git_ipfs_config_file))
